package net.andreaskluth;
import java.util.concurrent.CompletableFuture;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import com.google.common.base.Stopwatch;
/**
* Monitors a {@link CompletableFuture} and reports the time consumed to process
* the {@link CompletableFuture}, the initiating and executing thread and a
* description of the method executed.
*
* @author Andreas Kluth <mail@andreaskluth.net>
*/
@Aspect
@Component
public class AsyncTimedAspect {
private static final Log LOG = LogFactory.getLog(AsyncTimed.class);
@Pointcut("@annotation(net.andreaskluth.AsyncTimed)")
public void asyncTimedAnnotationPointcut() {
}
@Around("asyncTimedAnnotationPointcut()")
public Object methodsAnnotatedWithAsyncTime(final ProceedingJoinPoint joinPoint) throws Throwable {
return proceed(joinPoint);
}
private Object proceed(final ProceedingJoinPoint joinPoint) throws Throwable {
final Object result = joinPoint.proceed();
if (!isCompletableFuture(result)) {
return result;
}
final String description = joinPoint.toString();
final Stopwatch watch = Stopwatch.createStarted();
String initiatingThread = Thread.currentThread().getName();
return ((CompletableFuture<?>) result).thenApply(__ -> {
String executingThread = Thread.currentThread().getName();
watch.stop();
LOG.info("Timed: " + description + " ; " + watch.toString() + " ; Initiating Thread '" + initiatingThread
+ "' Executing Thread '" + executingThread + "'.");
return __;
});
}
private boolean isCompletableFuture(Object result) {
return CompletableFuture.class.isAssignableFrom(result.getClass());
}
}